<template>
  <div v-loading="state.loading">
    <QueryFrom v-if="$slots.queryItem" class="query-form" :query-form-data="props.queryFormData" @query="query">
      <template #queryItem>
        <slot name="queryItem"></slot>
      </template>
      <template v-if="$slots.moreQuery" #moreQuery>
        <slot name="moreQuery"></slot>
      </template>
    </QueryFrom>

    <div v-if="props.showTableTop" class="top-btn flex-row-center-between mgt5 mgb10 width-all">
      <div class="flex-row-center-left flex1">
        <el-button v-has-permi="[props.addPermi]" v-show="props.showTopAddBtn" type="primary" @click="handleUpdateOrAdd(props.addData, 'add')">新增</el-button>
        <el-button v-has-permi="[props.delPermi]" v-show="props.showDelBtn" type="danger" :disabled="!state.selectedList.length" @click="handleDeleteRowById(null)">删除</el-button>
        <slot name="topExtra" v-bind="state"></slot>
      </div>

      <div v-if="props.showTopRightIcon" class="flex-row-center-left gap10">
        <el-tooltip effect="dark" content="刷新" placement="top-end">
          <div class="icon-box flex-row-center-center">
            <SvgIcon name="reflesh" class="flex1" color="#9ab4f0" @click="query" />
          </div>
        </el-tooltip>
        <el-tooltip effect="dark" content="全屏" placement="top-end">
          <div class="icon-box flex-row-center-center">
            <SvgIcon name="expand" class="flex1" color="#9ab4f0" @click="handleExpandTable" />
          </div>
        </el-tooltip>
        <el-tooltip effect="dark" content="表格配置" placement="top-end">
          <div class="icon-box flex-row-center-center">
            <SvgIcon name="column-filter" class="flex1" color="#9ab4f0" @click="openFilterColumnDialog" />
          </div>
        </el-tooltip>
      </div>
    </div>

    <div :class="{ 'expand-table': state.expand, 'p-relate': !state.expand }">
      <el-tooltip v-if="state.expand" effect="dark" content="收起" placement="top-end">
        <div style="position: fixed; top: 10px; right: 5px; z-index: 200; border-radius: 5px" class="icon-box flex-row-center-center">
          <SvgIcon name="collapse" class="flex1" color="#9ab4f0" @click="handleCollapseTable" />
        </div>
      </el-tooltip>

      <vxe-table
        :key="state.currentTableKey"
        ref="tableEl"
        :data="props.dataListApi ? state.dataList : props.dataList"
        :column-config="{ resizable: true }"
        :scroll-y="{ enabled: true, gt: 25 }"
        align="center"
        border
        footer-row-class-name="vxetable-footer-row"
        :max-height="props.tableHeight ? props.tableHeight : state.tableHeigt"
        @checkbox-all="onSelectionChange"
        @checkbox-change="onSelectionChange"
        v-bind="$attrs"
      >
        <vxe-column v-for="item in mapFilterColumn" :key="item.field" v-bind="item" show-overflow="tooltip">
          <template v-if="item.slot" #default="slotProps">
            <slot :name="item.slot" v-bind="slotProps"></slot>
          </template>
        </vxe-column>

        <vxe-column v-if="props.showActionColumn" title="操作" fixed="right" :width="props.actionColumnWidth" header-align="center" align="center">
          <template #default="{ row }">
            <slot name="extraBtn1" :row="row"></slot>
            <el-button v-has-permi="[props.updatePermi]" v-show="$slots.dialogUpdateOrAdd" :disabled="handleDisableUpdate(row)" type="primary" link @click="handleUpdateOrAdd(row, 'update')">
              修改
            </el-button>
            <slot name="extraBtn2" :row="row"></slot>
            <el-button v-has-permi="[props.delPermi]" v-show="props.showDelBtn" :disabled="handleDisableDel(row)" type="danger" link @click="handleDeleteRowById(row)">删除</el-button>
            <slot name="extraBtn3" :row="row"></slot>
          </template>
        </vxe-column>

        <template #empty>
          <div class="flex-column-center-center">
            <SvgIcon :width="100" :height="100" name="empty-data" color="#606266" />
            <span>暂无数据~</span>
          </div>
        </template>
      </vxe-table>

      <div v-if="props.showPagination" class="pagination-box">
        <el-pagination
          v-show="props.dataListApi && !state.loading"
          class="pagination"
          v-model:current-page="queryParams.currentPage"
          v-model:page-size="queryParams.pageSize"
          :page-sizes="[10, 20, 50, 100]"
          layout="total, sizes, prev, pager, next, jumper"
          :total="state.total"
          @size-change="query"
          @current-change="query"
        />
      </div>
    </div>

    <!------------------------------------------ 新增、修改 ------------------------------------------------------->

    <DialogVue
      v-if="state.isShowDialog"
      ref="dialogEl"
      :width="props.dialogWidth"
      :title="state.dialogType === 'add' ? `新增${props.moduleName}` : `修改${props.moduleName}`"
      :api-method="state.dialogType === 'add' ? 'post' : 'put'"
      :form-data="state.dialogUpdateOrAddData"
      :api="props.updateOrAddApi"
      :rules="props.dialogFormRules"
      @closed="state.isShowDialog = false"
      @before-submit="onBeforeSubmit"
      @submit-success="query"
      @refactor-params="onRefactorDialogParams"
    >
      <slot name="dialogUpdateOrAdd" :row="state.dialogUpdateOrAddData"></slot>
    </DialogVue>

    <!------------------------------------------ 表格筛选 ------------------------------------------------------->

    <DialogVue v-if="props.showTopRightIcon" ref="dialogFilterColumnEl" :width="500" title="表格列显示筛选 (宽度无值为自适应)" @before-submit="handleConfirmFilter">
      <div id="filtertable">
        <vxe-table ref="tableFilterEl" :data="state.filterDialogData" border align="center" @checkbox-all="onFilterTableSelectionChange" @checkbox-change="onFilterTableSelectionChange">
          <vxe-column type="checkbox" width="80" />
          <vxe-column title="字段名">
            <template #default="{ row }">
              <div>{{ row.type === 'checkbox' ? '选择行' : row.type === 'index' ? '序号' : row.title }}</div>
            </template>
          </vxe-column>
          <vxe-column title="宽度" width="120">
            <template #default="{ row }">
              <el-input type="number" :min="50" v-model="row.width" />
            </template>
          </vxe-column>
          <vxe-column title="拖拽排序" width="80">
            <template #default>
              <SvgIcon class="my-handle" name="sort" />
            </template>
          </vxe-column>
        </vxe-table>
      </div>

      <template #btnSlot2>
        <el-button type="primary" @click="handleResetColumn"> 重置 </el-button>
      </template>
    </DialogVue>
  </div>
</template>

<script setup lang="ts">
  import { ref, toRefs, reactive, computed, onMounted, onUnmounted, onActivated, nextTick, type PropType } from 'vue'
  import type { VxeTableInstance } from 'vxe-table'
  import sortTable from 'sortablejs'
  import QueryFrom from '@/components/query-form'
  import DialogVue, { type DialogVueInstance, type ValidateFn } from '@/components/dialog'
  import { Ui } from '@/utils'
  import request from '@/service'
  import { clone, isEmpty } from 'xe-utils'
  import type { IVxueColumnConfig, IPropsFnObject } from '../index'
  import { useUserStore } from '@/store'
  import router from '@/router'

  interface IEmits {
    (e: 'before-open-dialog', type: IDialogType, data: any): void
    (e: 'before-submit', data: any, fn: ValidateFn): void
    (e: 'refactor-data', data: any[]): void
    (e: 'refactor-dialog-params', type: IDialogType, data: any, fn: ValidateFn): void
    (e: 'refactor-query-params', data: any): void
  }

  const emits = defineEmits<IEmits>()

  const onBeforeSubmit = (data: any, fn: ValidateFn) => {
    emits('before-submit', data, fn)
  }

  const onRefactorDialogParams = (data: any, fn: ValidateFn) => {
    emits('refactor-dialog-params', state.dialogType, data, fn)
  }

  const props = defineProps({
    queryFormData: {
      type: Object,
      default: () => ({})
    },
    columnConfig: {
      type: Array as PropType<any[]>,
      default: () => []
    },
    dataListApi: {
      type: String,
      default: ''
    },
    dataList: {
      type: Array,
      default: () => []
    },
    deleteApi: {
      type: String,
      default: ''
    },
    useParamsDelete: {
      type: Boolean,
      default: false
    },
    updateOrAddApi: {
      ype: String,
      default: ''
    },
    pageSize: {
      type: Number,
      default: 100
    },
    extraSubHeight: {
      type: Number,
      default: 0
    },
    addData: {
      type: Object,
      default: () => ({})
    },
    moduleName: {
      type: String,
      default: ''
    },
    dialogFormRules: {
      type: Object,
      default: () => ({})
    },
    actionColumnWidth: {
      type: Number,
      default: 180
    },
    showTableTop: {
      type: Boolean,
      default: true
    },
    showActionColumn: {
      type: Boolean,
      default: true
    },
    showDelBtn: {
      type: Boolean,
      default: true
    },
    showTopAddBtn: {
      type: Boolean,
      default: true
    },
    showTopRightIcon: {
      type: Boolean,
      default: true
    },
    showPagination: {
      type: Boolean,
      default: true
    },
    dialogWidth: {
      type: Number,
      default: 800
    },
    onActiveQuery: {
      type: Boolean,
      default: false
    },
    onMountedQuery: {
      type: Boolean,
      default: true
    },
    fnObject: {
      type: Object as PropType<IPropsFnObject>,
      default: () => ({})
    },
    addPermi: {
      type: String,
      default: ''
    },
    delPermi: {
      type: String,
      default: ''
    },
    updatePermi: {
      type: String,
      default: ''
    },
    tableHeight: {
      type: Number,
      default: 0
    }
  })

  const dialogEl = ref<DialogVueInstance | null>()
  const tableEl = ref<VxeTableInstance | null>()
  const dialogFilterColumnEl = ref<DialogVueInstance>()
  const tableFilterEl = ref<VxeTableInstance>()

  const { path: currentRouterPath } = router.currentRoute.value

  const { getByKey, setColumnPageFilter } = useUserStore()

  const defaultColumn = props.showTopRightIcon ? getByKey('columnPageFilter')?.[currentRouterPath] ?? props.columnConfig : props.columnConfig

  const queryParams = reactive({
    currentPage: 1,
    pageSize: props.pageSize
  })

  type IDialogType = 'add' | 'update'

  const state = reactive({
    loading: false,
    expand: false,
    currentTableKey: String(new Date().getTime()),
    filterColumn: defaultColumn,
    filterDialogData: [] as any[],
    total: 0,
    tableHeigt: 500,
    dataList: [] as any[], // 表格数据
    selectedList: [] as any[],
    dialogUpdateOrAddData: {} as any,
    dialogType: '' as IDialogType,
    isShowDialog: false
  })

  const mapFilterColumn = computed(() => (props.fnObject.mapColumn ? props.fnObject.mapColumn(state.filterColumn).filter(item => item.show) : state.filterColumn.filter((item: any) => item.show)))

  /**----------------------------methods----------------------------------------------------------------------- */

  /**禁止修改行 */
  const handleDisableUpdate = (data: any) => {
    if (!props.fnObject.disableUpdate) return false
    return props.fnObject.disableUpdate(data)
  }

  const handleDisableDel = (data: any) => {
    if (!props.fnObject.disableRowDel) return false
    return props.fnObject.disableRowDel(data)
  }

  /**
   * 获取内部tableel
   */
  const getTableEl = () => tableEl.value

  const handleExpandTable = () => {
    state.expand = true
    //@ts-ignore
    state.tableHeigt = '100%'
  }

  const handleCollapseTable = () => {
    state.expand = false
    calTableHeight()
  }

  /**
   * 勾选需要显示的列
   * @param v
   */
  const onFilterTableSelectionChange = (v: any) => {
    const { rowIndex, checked } = v
    if (!rowIndex && rowIndex !== 0) {
      state.filterDialogData.forEach((item: any) => (item.show = checked))
    } else {
      state.filterDialogData[rowIndex].show = checked
    }
  }

  /**
   * 打开筛选行
   */
  const openFilterColumnDialog = async () => {
    state.filterDialogData = clone(state.filterColumn, true)
    dialogFilterColumnEl.value?.handleOpen()

    await nextTick()

    setTimeout(() => {
      const el = document.querySelector('#filtertable .body--wrapper>.vxe-table--body tbody') as HTMLTableElement

      sortTable.create(el, {
        animation: 300,
        handle: '.my-handle',
        onEnd({ newIndex, oldIndex }) {
          const arr = state.filterDialogData
          const currRow = state.filterDialogData[oldIndex as number]
          arr.splice(oldIndex as number, 1)
          arr.splice(newIndex as number, 0, currRow)
        }
      })

      if (tableFilterEl.value) {
        // 设置选中状态
        const list = state.filterDialogData.filter((item: any) => item.show)
        tableFilterEl.value.setCheckboxRow(list, true)
      }
    }, 300)
  }

  /**
   * 确认筛选行
   * @param _
   * @param fn
   */
  const handleConfirmFilter = (_: any, fn: ValidateFn) => {
    fn(false)

    const showColumn: IVxueColumnConfig[] = state.filterDialogData.filter((item: any) => item.show)

    if (!showColumn.length) return Ui.useMessage('warning', '请至少选择一列!')

    state.filterColumn = state.filterDialogData

    state.filterColumn.forEach((item: any) => {
      item.width = item.width ? parseInt(item.width as any) : item.width
    })

    setColumnPageFilter(currentRouterPath, state.filterColumn)

    tableEl.value?.loadColumn(showColumn as any)

    state.currentTableKey = String(new Date().getTime())

    dialogFilterColumnEl.value?.handleClose()
  }

  /**
   * 重置筛选行
   */
  const handleResetColumn = () => {
    const columnMap = getByKey('columnPageFilter')
    if (isEmpty(columnMap)) {
      dialogFilterColumnEl.value?.handleClose()
      return
    }
    if (!columnMap[currentRouterPath]) {
      dialogFilterColumnEl.value?.handleClose()
      return
    }

    delete columnMap[currentRouterPath]

    state.filterColumn = props.columnConfig

    setColumnPageFilter(currentRouterPath, state.filterColumn)

    tableEl.value?.loadColumn(state.filterColumn as any)
    state.currentTableKey = String(new Date().getTime())

    dialogFilterColumnEl.value?.handleClose()
  }

  /**
   *
   * @param list 选中行
   */
  const onSelectionChange = () => {
    const list = tableEl.value?.getCheckboxRecords(true) as any[]
    state.selectedList = list
  }

  /**
   * 列表新增或修改
   * @param data
   * @param type
   */
  const handleUpdateOrAdd = async (data: any, type: IDialogType) => {
    state.dialogUpdateOrAddData = type === 'add' ? data : clone(data, true)
    state.dialogType = type
    emits('before-open-dialog', type, state.dialogUpdateOrAddData)
    state.isShowDialog = true

    await nextTick()

    dialogEl.value?.handleOpen()
  }

  const setDialogLoading = (load: boolean) => dialogEl.value?.setLoading(load)

  /**
   * 删除数据
   * @param row
   */
  const handleDeleteRowById = (row: any) => {
    const handleConfirm = () => {
      const { deleteApi, useParamsDelete } = props
      // id 拼接在路径后面
      const api = useParamsDelete ? `${deleteApi}/${row.id}` : deleteApi
      const idList = row ? [row.id] : state.selectedList.map(item => item.id)

      request.delete(api, useParamsDelete ? null : idList).then(res => {
        if (res.code !== 0) return Ui.useMessage('error', res.msg)

        Ui.useMessage('success', res.msg)
        query()
      })
    }

    Ui.useMessageBox({
      handleConfirm
    })
  }

  /**
   * 查询数据
   */
  const query = () => {
    if (!props.dataListApi) return
    state.loading = true
    const params = {
      current: queryParams.currentPage,
      size: queryParams.pageSize,
      ...props.queryFormData
    }

    if (!props.showPagination) {
      params.current = 1
      params.size = 99999
    }

    emits('refactor-query-params', params)

    request
      .get(props.dataListApi, params)
      .then(res => {
        if (res.code !== 0) return Ui.useMessage('error', res.msg)
        if (res.data?.records && res.data.records?.length) {
          emits('refactor-data', res.data.records)
          state.dataList = res.data.records
          state.total = res.data.total
        } else if (res.data && res.data.length) {
          emits('refactor-data', res.data)
          state.dataList = res.data
        } else {
          state.total = 0
          state.dataList = []
        }

        nextTick(() => {
          onSelectionChange()
        })
      })
      .finally(() => (state.loading = false))
  }

  const calTableHeight = () => {
    const queryFormEl = document.querySelector('.query-form')
    const topBtnEl = document.querySelector('.top-btn')
    state.tableHeigt = window.innerHeight - (queryFormEl?.clientHeight ?? 0) - (topBtnEl?.clientHeight ?? 0) - 220 - props.extraSubHeight
  }

  const handleCloseDialog = () => dialogEl.value?.handleClose()

  const setupInit = () => {
    if (props.onMountedQuery) {
      query()
    }
  }

  /**----------------------------lifecycle----------------------------------------------------------------------- */

  setupInit()

  onMounted(() => {
    window.addEventListener('resize', calTableHeight)

    calTableHeight()
  })

  onActivated(() => {
    if (props.onActiveQuery) query()
  })

  onUnmounted(() => {
    window.removeEventListener('resize', calTableHeight)
  })

  defineExpose({
    query,
    handleUpdateOrAdd,
    getTableEl,
    setDialogLoading,
    handleCloseDialog,
    ...toRefs(state),
    ...toRefs(queryParams)
  })
</script>

<style scoped lang="less">
  .pagination-box {
    position: absolute;
    bottom: -45px;
    right: 20px;
  }

  .icon-box {
    width: 30px;
    height: 30px;
    border: 1px solid #9ab4f0;
    cursor: pointer;
    border-radius: 50%;
  }
  .icon-box:hover {
    background-color: #ecf5ff;
  }

  .expand-table {
    position: fixed;
    height: 100%;
    width: 100%;
    z-index: 99;
    background: #fff;
    top: 0;
    left: 0;
  }
  .p-relate {
    position: relative;
  }
</style>
